import os
import time
from datetime import datetime
import pytest
from selenium import webdriver
from selenium.webdriver import ActionChains, Keys
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from time import sleep

from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.wait import WebDriverWait


@pytest.fixture(scope="function")
def driver():
    service = Service(
        #提交最终代码脚本时，请将驱动路径换回官方路径"C:\\Users\\86153\\AppData\\Local\\Google\\Chrome\\Application\\chromedriver.exe"
        executable_path="D:\\Software\\Chrome\\Driver\\chromedriver.exe")
    driver = webdriver.Chrome(service=service)
    driver.get("https://www.ctrip.com")
    driver.maximize_window()
    yield driver
    driver.quit()


class TestCtripFlight:
    #  test-code-start
    # 进入机票购买页面 组件定位
    __ENTER_TICKETS = (By.XPATH, "//button[@aria-label='机票 按回车键打开菜单' and contains(@class, 'lsn_top_nav_qdgwe')]")
    # 出发机场/城市 组件定位
    __LEAVE_PLACE_INPUT = (By.XPATH, "/html/body/div[1]/div[2]/div[2]/div/div[1]/div/div[1]/div/form/div/div/div/div[2]/div[1]/div/div[1]/div/div/div[1]/input")
    # 到达机场/城市 组件定位
    __ARRIVE_PLACE_INPUT = (By.XPATH, "/html/body/div[1]/div[2]/div[2]/div/div[1]/div/div[1]/div/form/div/div/div/div[2]/div[1]/div/div[3]/div/div/div[1]/input")
    # 出发日期 组件定位
    __LEAVE_DATA = (By.XPATH,"/html/body/div[1]/div[2]/div[2]/div/div[1]/div/div[1]/div/form/div/div/div/div[2]/div[2]/div/div[1]/span/div/div/div/input")
    # 是否携带小孩 组件定位
    __HAVE_CHILD = (By.XPATH,"/html/body/div[1]/div[2]/div[2]/div/div[1]/div/div[1]/div/form/div/div/div/div[2]/div[3]/div/div/div/div/div/div/div[1]")
    # 是否携带婴儿 组件定位
    __HAVE_BABY = (By.XPATH,"/html/body/div[1]/div[2]/div[2]/div/div[1]/div/div[1]/div/form/div/div/div/div[2]/div[3]/div/div/div/div/div/div/div[2]")
    # 舱位选择定位去
    __CLASS_ROOM = {
        "menu":(By.XPATH,"/html/body/div[1]/div[2]/div[2]/div/div[1]/div/div[1]/div/form/div/div/div/div[1]/div/div/div[1]"),
        "all":(By.XPATH,"/html/body/div[1]/div[2]/div[2]/div/div[1]/div/div[1]/div/form/div/div/div/div[1]/div/div/div[2]/ul/li[1]"),
        "economy":(By.XPATH,"/html/body/div[1]/div[2]/div[2]/div/div[1]/div/div[1]/div/form/div/div/div/div[1]/div/div/div[2]/ul/li[2]"),
        "business":(By.XPATH,"/html/body/div[1]/div[2]/div[2]/div/div[1]/div/div[1]/div/form/div/div/div/div[1]/div/div/div[2]/ul/li[3]")
    }
    # 输入出发机场后第一个结果组件的组件定位
    __LEAVE_AIRPOPT_SELECTOR = (By.XPATH,"/html/body/div[1]/div[2]/div[2]/div/div[1]/div/div[1]/div/form/div/div/div/div[2]/div[1]/div/div[1]/div/div/div[2]/div[1]//div[1]")
    # 输入到达机场后第一个结果组件的组件定位
    __ARRIVE_AIRPORT_SELECTOR = (By.XPATH,"/html/body/div[1]/div[2]/div[2]/div/div[1]/div/div[1]/div/form/div/div/div/div[2]/div[1]/div/div[3]/div/div/div[2]/div[1]//div[1]")
    # 单程旅行组件定位
    __SIGNAL_TRAVE = (By.XPATH,"/html/body/div[1]/div[2]/div[2]/div/div[1]/div/div[1]/div/form/div/div/div/div[1]/ul/li[1]/span/i[1]")
    # 往返旅行组件定位
    __ROUND_TRAVE = (By.XPATH,"/html/body/div[1]/div[2]/div[2]/div/div[1]/div/div[1]/div/form/div/div/div/div[1]/ul/li[2]/span/i[2]")
    # 搜索按钮组件
    __SEARCH_BUTTON = (By.XPATH,"/html/body/div[1]/div[2]/div[2]/div/div[1]/div/div[1]/div/form/div/button")
    # 错误信息提示组件
    __ERROR_INFO = (By.XPATH,"/html/body/div[1]/div[2]/div[2]/div/div[1]/div/div[1]/div/form/div/div/div/div[2]/div[1]/div/div[1]/div/div/div[2]/div[1]")
    # 国际航班中乘客人数填写下拉框组件
    __PERSON_SELECTOR = (By.XPATH,"/html/body/div[1]/div[2]/div[2]/div/div[1]/div/div[1]/div/form/div/div/div/div[2]/div[3]/div/div/div/div[1]/div/div/i[1]")
    # 人数添加按钮
    __PERSON_ADD_BUTTON = {
        "adult":(By.XPATH,"/html/body/div[1]/div[2]/div[2]/div/div[1]/div/div[1]/div/form/div/div/div/div[2]/div[3]/div/div/div/div[2]/div[1]/div[2]/div[3]"),
        "child":(By.XPATH,"/html/body/div[1]/div[2]/div[2]/div/div[1]/div/div[1]/div/form/div/div/div/div[2]/div[3]/div/div/div/div[2]/div[2]/div[2]/div[3]"),
        "baby":(By.XPATH,"/html/body/div[1]/div[2]/div[2]/div/div[1]/div/div[1]/div/form/div/div/div/div[2]/div[3]/div/div/div/div[2]/div[3]/div[2]/div[3]")
    }
    __SUBMIT_PERSON_NUMBER = (By.XPATH,"/html/body/div[1]/div[2]/div[2]/div/div[1]/div/div[1]/div/form/div/div/div/div[2]/div[3]/div/div/div/div[2]/div[4]/a[1]")
    # 查询结果列表
    __RESULT_TICKETS = (By.XPATH,"/html/body/div[1]/div[2]/div[2]/div/div[3]/div[3]/div[2]/span")


    # 行为
    # 最长的强制等待时间
    timeout = 10

    # 自定义封装查询组件函数，包含指定时间的显示等待
    def find_element(self,driver,by,value):
        try:
            WebDriverWait(driver, self.timeout).until(
                expected_conditions.visibility_of_element_located((by, value))
            )
            return WebDriverWait(driver, self.timeout).until(
                expected_conditions.element_to_be_clickable((by, value))
            )
        except Exception as e:
            print(f"元素定位失败！定位方式：{by}，元素路径：{value}，错误详情：{type(e).__name__}: {str(e)}")
            return None


    #  请在此处插入python+selenium代码
    @pytest.mark.parametrize(
        "leave_place,arrive_place,classRoom,signal_trave,have_child,have_baby",
        [
            ["北京","成都","economy",True,True,False],
            ["上海","广州","economy",True,True,False]
        ],ids=["CtripFlight_R001_001","CtripFlight_R001_002"]
    )
    def test_CtripFlight_R001(self,driver,request,leave_place,arrive_place,classRoom,signal_trave,have_child,have_baby):
        """
        从携程首页进入机票查询页面，查询指定出发地、到达地等信息的机票信息，并截图
        :param driver: 驱动
        :param request: pytest测试请求数据，可以用于获取当前测试的ids值
        :param leave_place: 出发地
        :param arrive_place: 到达地
        :param classRoom: 飞机舱位(economy:经济舱、business:商务舱、all:不限)
        :param signal_trave: 是否为单程
        :param have_child: 是否携带小孩
        :param have_baby: 是否携带婴儿
        :return: 无返回值
        """
        status = True  # 用于保存脚本运行结果
        msg = None  # 用于接收运行过程中的错误信息
        actions = ActionChains(driver)
        try:
            # 进入购票页面
            enter_ele = self.find_element(driver,*self.__ENTER_TICKETS)
            actions.move_to_element(enter_ele).click(enter_ele).perform()

            # 选择单程或往返
            if signal_trave:
                self.find_element(driver,*self.__SIGNAL_TRAVE).click()
            else:
                self.find_element(driver,*self.__ROUND_TRAVE).click()

            # 输入出发地和到达地
            leave_input = self.find_element(driver,*self.__LEAVE_PLACE_INPUT)
            arrive_input = self.find_element(driver,*self.__ARRIVE_PLACE_INPUT)
            actions.click(leave_input).send_keys(leave_place).perform()
            self.find_element(driver,*self.__LEAVE_AIRPOPT_SELECTOR).click()
            actions.click(arrive_input).send_keys(arrive_place).perform()
            self.find_element(driver,*self.__ARRIVE_AIRPORT_SELECTOR).click()

            # 输入出行日期
            target_data_ele = self.find_element(driver,By.XPATH,"/html/body/div[3]/div/div[2]/div[2]/div[2]/div/div[4]/div[5]")
            target_data_ele.click()

            # 输入携带孩子和婴儿情况
            if have_baby:
                baby_selector = self.find_element(driver,*self.__HAVE_BABY)
                if not baby_selector.is_selected():
                    baby_selector.click()
            if have_child:
                child_selector = self.find_element(driver,*self.__HAVE_CHILD)
                if not child_selector.is_selected():
                    child_selector.click()

            # 选择舱位
            if classRoom == "all" or classRoom == "economy" or classRoom == "business":
                self.find_element(driver,*self.__CLASS_ROOM["menu"]).click()
                self.find_element(driver,*self.__CLASS_ROOM[classRoom]).click()
            else:
                print(f"[参数错误]用户选择的{classRoom}不存在，舱位错误")


            # 执行搜索结果
            self.find_element(driver,*self.__SEARCH_BUTTON).click()
            self.find_element(driver,*self.__RESULT_TICKETS)
        except Exception as e:
            status = False # 脚本运行异常
            msg = str(e)
        assert status , f"查询失败，失败原因{msg}"
        # 通过 request.node.callspec.id 获取当前用例的 ids
        case_id = request.node.callspec.id
        self.take_screenshot(driver,case_id)



    @pytest.mark.parametrize(
        "leave_place",
        [
            "鲁尚武"
        ],ids=["CtripFlight_R002_001"]
    )
    def test_CtripFlight_R002(self,driver,request,leave_place):
        """
        测试需求点2，输入一个不正确的地名，页面显示“对不起，暂不支持该地点“，并截图
        :param driver: 驱动
        :param leave_place: 出发地
        :return:
        """
        status = True
        msg = None
        try:
            actions = ActionChains(driver)
            # 进入购票页面
            enter_ele = self.find_element(driver,*self.__ENTER_TICKETS)
            actions.move_to_element(enter_ele).click(enter_ele).perform()
            leave_place_ele = self.find_element(driver,*self.__LEAVE_PLACE_INPUT)
            actions.click(leave_place_ele).send_keys(leave_place).perform()
            error_text = self.find_element(driver,*self.__ERROR_INFO).text
        except Exception as e:
            status = False
            msg = f"{type(e).__name__}: {str(e)}"
        assert status and error_text == "对不起，暂不支持该地点",f"测试失败，原因:{msg}"
        case_id = request.node.callspec.id
        self.take_screenshot(driver,case_id)


    @pytest.mark.parametrize(
        "leave_place,arrive_place,adult_num,child_num,baby_num",
        [
            ["北京","东京",2,1,1],
            ["上海","大阪",2,1,1]
        ],ids=["CtripFlight_R003_001","CtripFlight_R003_002"]
    )
    def test_CtripFlight_R003(self,driver,request,leave_place,arrive_place,adult_num,child_num,baby_num):
        """
        以指定的乘机人进行查询国际航班机票信息
        :param driver: 驱动
        :param request: pytest测试数据
        :param leave_place: 出发地
        :param arrive_place: 到达地
        :param adult_num: 成年人数量
        :param child_num: 孩子数量
        :param baby_num: 婴儿数量
        :return: 无返回值
        """
        status = True
        msg = None
        try:
            actions = ActionChains(driver)
            # 进入购票页面
            enter_ele = self.find_element(driver,*self.__ENTER_TICKETS)
            actions.move_to_element(enter_ele).click(enter_ele).perform()
            # 选择单程
            self.find_element(driver,*self.__SIGNAL_TRAVE).click()

            # 输入出发地和到达地
            leave_input = self.find_element(driver,*self.__LEAVE_PLACE_INPUT)
            arrive_input = self.find_element(driver,*self.__ARRIVE_PLACE_INPUT)
            actions.click(leave_input).send_keys(leave_place).perform()
            self.find_element(driver,*self.__LEAVE_AIRPOPT_SELECTOR).click()
            actions.click(arrive_input).send_keys(arrive_place).perform()
            self.find_element(driver,*self.__ARRIVE_AIRPORT_SELECTOR).click()

            # 输入出行日期
            target_data_ele = self.find_element(driver,By.XPATH,"/html/body/div[3]/div/div[2]/div[2]/div[2]/div/div[4]/div[5]")
            target_data_ele.click()

            # 输入乘机人数
            if adult_num > 1 or child_num > 0 or baby_num > 0:
                self.find_element(driver,*self.__PERSON_SELECTOR).click()
                while adult_num > 1:
                    self.find_element(driver,*self.__PERSON_ADD_BUTTON['adult']).click()
                    adult_num = adult_num - 1
                while child_num > 0:
                    self.find_element(driver,*self.__PERSON_ADD_BUTTON['child']).click()
                    child_num = child_num - 1
                while baby_num > 0:
                    self.find_element(driver,*self.__PERSON_ADD_BUTTON['baby']).click()
                    baby_num = baby_num - 1
                self.find_element(driver,*self.__SUBMIT_PERSON_NUMBER).click()

            # 查询结果
            self.find_element(driver,*self.__SEARCH_BUTTON).click()
            res = self.find_element(driver,*self.__RESULT_TICKETS)
        except Exception as e:
            status = False
            msg = f"{type(e).__name__}: {str(e)}"
        assert status ,f"测试失败，原因：{msg}"
        case_id = request.node.callspec.id
        self.take_screenshot(driver,case_id)

    #  test-code-end

    @staticmethod
    def take_screenshot(driver, file_name):
        timestamp = datetime.now().strftime("%H%M%S%d%f")
        timestamped_file_name = f"{timestamp}_{file_name}.png"
        screenshots_dir = "screenshots"
        if not os.path.exists(screenshots_dir):
            os.makedirs(screenshots_dir)
        screenshot_file_path = os.path.join(screenshots_dir, timestamped_file_name)
        driver.save_screenshot(screenshot_file_path)

